package cn.libinal.scloud.base.service.impl;

import cn.libinal.scloud.base.common.service.impl.AbstractJpaServiceImpl;
import cn.libinal.scloud.base.entity.Role;
import cn.libinal.scloud.base.entity.User;
import cn.libinal.scloud.base.repository.RoleRepository;
import cn.libinal.scloud.base.repository.UserRepository;
import cn.libinal.scloud.base.service.UserService;
import com.querydsl.core.types.Predicate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * @author libinal
 * created at 2018/11/4 20:11
 */
@Service
public class UserServiceImpl extends AbstractJpaServiceImpl<User, UserRepository> implements UserService {

    private final RoleRepository roleRepository;
    private final PasswordEncoder passwordEncoder;

    public UserServiceImpl(UserRepository repository, RoleRepository roleRepository, PasswordEncoder passwordEncoder) {
        super(repository);
        this.roleRepository = roleRepository;
        this.passwordEncoder = passwordEncoder;
    }

    @Override
    public User create(User user) {
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        return super.create(user);
    }

    @Override
    public Predicate buildPagingPredicates(User user) {
        return null;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void relateRoles(String userId, List<String> roleIds) {
        Assert.notNull(userId, "分配角色时用户id不能为空");
        Assert.notEmpty(roleIds, "分配角色时角色id不能为空");
        User user = findById(userId);
        List<String> oldRoles = user.getRoles().stream().map(Role::getId).collect(Collectors.toList());

        // 先删除解除关系的角色
        oldRoles.stream().filter(id -> !roleIds.contains(id))
                .map(id -> CompletableFuture.supplyAsync(() -> roleRepository.findById(id)))
                .map(future -> future.thenApply(Optional::get))
                .map(future -> future.thenAccept(role -> user.getRoles().remove(role)))
                .forEach(CompletableFuture::join);

        // 再新增新的角色
        roleIds.stream().filter(id -> !oldRoles.contains(id))
                .map(id -> CompletableFuture.supplyAsync(() -> roleRepository.findById(id)))
                .map(future -> future.thenApply(Optional::get))
                .map(future -> future.thenAccept(role -> user.getRoles().add(role)))
                .forEach(CompletableFuture::join);

        repository.save(user);
    }

    @Override
    public User findByUsername(String username) {
        return repository.findByUsername(username);
    }
}
